# JVM 规范 完整指南-4.JVM[Class]文件

本章节基于 [JDK 25 (2025)](https://docs.oracle.com/javase/specs/index.html) 同步发布的 JVM 规范进行翻译学习

## 4. Java 虚拟机类文件格式

本章描述 Java 虚拟机的类文件格式。每个类文件包含单个类、接口或模块的定义。尽管一个类、接口或模块不一定需要字面意义上地存在于一个文件中（例如，该类可能是由类加载器生成的），但我们将通俗地把类、接口或模块的任何有效表示形式称为类文件格式。

一个类文件由一串 8 位字节构成。16 位和 32 位的量分别通过读取两个和四个连续的 8 位字节来构造。多字节数据项总是以大端序存储，即高位字节在前。本章定义数据类型 u1、u2 和 u4 来分别表示一个无符号的一字节、二字节或四字节量。

在 Java SE 平台 API 中，类文件格式由接口 java.io.DataInput 和 java.io.DataOutput 以及诸如 java.io.DataInputStream 和 java.io.DataOutputStream 等类支持。例如，u1、u2 和 u4 类型的值可以通过 java.io.DataInput 接口的 readUnsignedByte、readUnsignedShort 和 readInt 等方法读取。

本章使用以类 C 结构体表示法编写的伪结构来展示类文件格式。为了避免与类、类实例的字段等混淆，描述类文件格式的结构体内容被称为项。连续的项按顺序存储在类文件中，没有填充或对齐。

由零个或多个可变大小项组成的表被用在几个类文件结构中。尽管我们使用类 C 数组语法来引用表项，但表是可变大小结构的流这一事实意味着无法将表索引直接转换为表中的字节偏移量。

当我们称一个数据结构为数组时，它由零个或多个连续固定大小的项组成，并且可以像数组一样进行索引。

本章中对 ASCII 字符的引用应解释为对应于该 ASCII 字符的 Unicode 码点。

### 4.1 Class 文件结构（The ClassFile Structure）

```c
ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
```

ClassFile 结构中的各项如下：

**magic**

magic 项提供标识类文件格式的魔数；其值为 0xCAFEBABE。

**minor_version, major_version**

minor_version 和 major_version 项的值分别是该 class 文件的次版本号和主版本号。主版本号和次版本号共同决定了类文件格式的版本。如果一个 class 文件的主版本号为 M，次版本号为 m，我们将其类文件格式的版本表示为 M.m。

major_version 项必须是 45 到 69（包含）之间的值。一个符合 Java SE 25 规范的 Java 虚拟机实现必须精确支持这些主版本号。

- 对于主版本号 (major_version) 为 56 或以上的 class 文件，其次版本号 (minor_version) 必须为 0 或 65535。
- 对于主版本号在 45 到 55（包含）之间的 class 文件，其次版本号可以是任何值。

版本号为 69.65535 的 class 文件依赖于 Java SE 25 的预览特性（§1.5.1）。这样的 class 文件只有在用户通过宿主系统指示启用了预览特性时才能被加载。

版本号为 M.65535（其中 56 ≤ M < 69）的 class 文件依赖于旧版 Java SE 平台的预览特性。这样的 class 文件不能被加载，无论预览特性是否启用。它只能由符合该旧版规范的 Java 虚拟机实现加载。

任何其他版本号的 class 文件不依赖于预览特性，无论预览特性是否启用都可以被加载。

**constant_pool_count**

constant_pool_count 项的值等于 constant_pool 表中的条目数加一。一个 constant_pool 索引被认为是有效的，当且仅当它大于零且小于 constant_pool_count，但 §4.4.5 中提到的 long 和 double 类型的常量除外。

**constant_pool[]**

constant_pool 是一个结构表（§4.4），表示各种字符串常量、类和接口名称、字段名称以及其它在 ClassFile 结构及其子结构中引用的常量。每个 constant_pool 表条目的格式由其第一个 "tag" 字节指示。

constant_pool 表的索引从 1 到 constant_pool_count - 1。

**access_flags**

access_flags 项的值是一个标志位掩码，用于表示该类或接口的访问权限和属性。每个标志位被设置时的含义在表 4.1-B 中规定。

**表 4.1-B. 类访问和属性修饰符**

| 标志名称           | 值     | 含义解释                                                |
| :----------------- | :----- | :------------------------------------------------------ |
| **ACC_PUBLIC**     | 0x0001 | 声明为 `public`；可以从其包外部访问。                   |
| **ACC_FINAL**      | 0x0010 | 声明为 `final`；不允许有子类。                          |
| **ACC_SUPER**      | 0x0020 | 当被 `invokespecial` 指令调用时，需要特殊处理超类方法。 |
| **ACC_INTERFACE**  | 0x0200 | 是一个接口，而不是类。                                  |
| **ACC_ABSTRACT**   | 0x0400 | 声明为 `abstract`；不能被实例化。                       |
| **ACC_SYNTHETIC**  | 0x1000 | 声明为 `synthetic`（合成的）；在源代码中不出现。        |
| **ACC_ANNOTATION** | 0x2000 | 声明为注解接口。                                        |
| **ACC_ENUM**       | 0x4000 | 声明为枚举类。                                          |
| **ACC_MODULE**     | 0x8000 | 是一个模块，而不是类或接口。                            |

ACC_MODULE 标志表明该类文件定义的是一个模块，而不是类或接口。如果设置了 ACC_MODULE 标志，则适用于该类文件的特殊规则在本节末尾给出。如果未设置 ACC_MODULE 标志，则紧接本段下方的规则适用于该类文件。

一个接口通过设置 ACC_INTERFACE 标志来区分。如果未设置 ACC_INTERFACE 标志，则该类文件定义的是一个类，而不是接口或模块。

如果设置了 ACC_INTERFACE 标志，则 ACC_ABSTRACT 标志也必须被设置，并且 不得 设置 ACC_FINAL、ACC_SUPER、ACC_ENUM 和 ACC_MODULE 标志。

如果未设置 ACC_INTERFACE 标志，则除了 ACC_ANNOTATION 和 ACC_MODULE 之外，表 4.1-B 中的任何其他标志都可以被设置。但是，这样的类文件不得同时设置其 ACC_FINAL 和 ACC_ABSTRACT 标志（JLS §8.1.1.2）。

ACC_SUPER 标志指示，如果 invokespecial 指令（§invokespecial）出现在此类或接口中，它将表达两种替代语义中的哪一种。编译到 Java 虚拟机指令集的编译器应设置 ACC_SUPER 标志。在 Java SE 8 及更高版本中，Java 虚拟机认为每个类文件都设置了 ACC_SUPER 标志，而不管该类文件中该标志的实际值以及类文件的版本如何。

ACC_SUPER 标志的存在是为了与旧版 Java 编程语言编译器编译的代码保持向后兼容。在 JDK 1.0.2 之前，编译器生成的 access_flags 中，现在代表 ACC_SUPER 的标志没有被赋予任何意义，并且 Oracle 的 Java 虚拟机实现会忽略该标志（如果它被设置）。

ACC_SYNTHETIC 标志表明该类或接口是由编译器生成的，并且在源代码中不出现。

一个注解接口（JLS §9.6）必须设置其 ACC_ANNOTATION 标志。如果设置了 ACC_ANNOTATION 标志，则 ACC_INTERFACE 标志也必须被设置。

ACC_ENUM 标志表明该类或其超类被声明为枚举类（JLS §8.9）。

access_flags 项中未在表 4.1-B 中分配的所有位都保留供将来使用。它们在生成的类文件中应设置为零，并且应被 Java 虚拟机实现忽略。

**this_class**

this_class 项的值必须是 constant_pool 表中的一个有效索引。该索引处的常量池条目必须是一个 CONSTANT_Class_info 结构（§4.4.1），表示该类文件定义的类或接口。

**super_class**

对于一个类，super_class 项的值必须为零，或者必须是 constant_pool 表中的一个有效索引。

- 如果 super_class 项的值非零，则该索引处的常量池条目必须是一个 CONSTANT_Class_info 结构，表示该类文件定义的类的直接超类。该直接超类及其任何超类都不得在其 ClassFile 结构的 access_flags 项中设置 ACC_FINAL 标志。
- 如果 super_class 项的值为零，则该类文件必须表示 Object 类，这是唯一没有直接超类的类或接口。

对于一个接口，super_class 项的值必须始终是 constant_pool 表中的一个有效索引。该索引处的常量池条目必须是一个 CONSTANT_Class_info 结构，表示 Object 类。

**interfaces_count**

interfaces_count 项的值给出了此类或接口类型的直接超接口的数量。

**interfaces[]**

interfaces 数组中的每个值都必须是 constant_pool 表中的一个有效索引。对于每个 interfaces[i]（其中 0 ≤ i < interfaces_count），该值处的常量\_pool 条目必须是一个 CONSTANT_Class_info 结构，表示作为此类或接口类型的直接超接口的一个接口，顺序与源代码中给出的从左到右的顺序一致。

**fields_count**

fields_count 项的值给出了 fields 表中 field_info 结构的数量。field_info 结构表示由此类或接口类型声明的所有字段，包括类变量和实例变量。

**fields[]**

fields 表中的每个值必须是一个 field_info 结构（§4.5），给出了此类或接口中一个字段的完整描述。fields 表仅包括由此类或接口声明的字段。它不包括表示从超类或超接口继承的字段的项。

**methods_count**

methods_count 项的值给出了 methods 表中 method_info 结构的数量。

**methods[]**

methods 表中的每个值必须是一个 method_info 结构（§4.6），给出了此类或接口中一个方法的完整描述。如果在 method_info 结构的 access_flags 项中既未设置 ACC_NATIVE 也未设置 ACC_ABSTRACT 标志，则还会提供实现该方法的 Java 虚拟机指令。

method_info 结构表示由此类或接口类型声明的所有方法，包括实例方法、类方法、实例初始化方法（§2.9.1）以及任何类或接口初始化方法（§2.9.2）。methods 表不包括表示从超类或超接口继承的方法的项。

**attributes_count**

attributes_count 项的值给出了此类的 attributes 表中属性的数量。

**attributes[]**

attributes 表中的每个值必须是一个 attribute_info 结构（§4.7）。

本规范定义的、出现在 ClassFile 结构的 attributes 表中的属性在表 4.7-C 中列出。

关于定义出现在 ClassFile 结构的 attributes 表中的属性的规则在 §4.7 中给出。

关于 ClassFile 结构的 attributes 表中非预定义属性的规则在 §4.7.1 中给出。

**模块 (ACC_MODULE) 的特殊结构规则**

如果在 access_flags 项中设置了 ACC_MODULE 标志，则 ClassFile 结构的其余部分必须严格遵守以下规则：

- access_flags: 只能设置 ACC_MODULE (0x8000) 标志，不能设置任何其他标志。
- major_version, minor_version: 版本号必须 ≥ 53.0（即对应 Java SE 9 及以上平台）。
- this_class: 必须指向常量池中一个代表 module-info 的 CONSTANT_Class_info 结构。这标识了该结构定义的是一个模块。
- super_class: 必须为 0（模块没有超类概念）。
- interfaces_count: 必须为 0（模块不实现接口）。
- fields_count: 必须为 0（模块不能声明字段）。
- methods_count: 必须为 0（模块不能声明方法）。
- attributes:
  - 必须包含一个且仅一个 Module 属性。该属性定义了模块的核心信息，如依赖、导出包等。
  - 除了以下明确允许的预定义属性外，不得包含其他任何预定义属性（§4.7）：
    - Module (必须存在)
    - ModulePackages
    - ModuleMainClass
    - InnerClasses
    - SourceFile
    - SourceDebugExtension
    - RuntimeVisibleAnnotations
    - RuntimeInvisibleAnnotations

### 4.2 命名（Names）

### 4.2.1 Binary Class and Interface Names
